package commons.base.rpc;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class RpcInvoker {

	private static final Logger log = LogManager.getLogger(RpcInvoker.class);
	
	private static MethodHandles.Lookup lookup = MethodHandles.lookup();
	private Map<String, MethodHandle> methodHandleMap = new ConcurrentHashMap<String, MethodHandle>();
	private Map<String, Object> implMap = new ConcurrentHashMap<String, Object>();
	
	public MethodHandle getMethodHandle(String key){
		return methodHandleMap.get(key);
	}
	
	public boolean contains(String key){
		return methodHandleMap.containsKey(key);
	}
	
	public void addService(Class<?> interfaceClazz, Object impl){
		for(Method m : interfaceClazz.getMethods()){
			try {
				String key = interfaceClazz.getSimpleName() + "_" + m.getName();
				
				MethodHandle mh = lookup.findVirtual(impl.getClass(), m.getName(), MethodType.methodType(m.getReturnType(), m.getParameterTypes()));
				
				methodHandleMap.put(key, mh);
				implMap.put(key, impl);
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
		}
	}
	
	public Object doService(String key, List<Object> args) throws Throwable{
		if(methodHandleMap.containsKey(key) && implMap.containsKey(key)){
			if((args != null) && (!args.isEmpty())){
				List<Object> tmp = new ArrayList<Object>();
				tmp.add(implMap.get(key));
				tmp.addAll(args);
				
				return methodHandleMap.get(key).invokeWithArguments(tmp.toArray());
			}
			
			return methodHandleMap.get(key).invoke(implMap.get(key));
		}
		
		return null;
	}
}
